﻿//
// Copyright 2024 Exhunter Technology Co.Ltd2.
//

using System;

namespace X18.Framework.Core.Hash
{
    /// <summary>
    /// MurmurHash算法
    /// </summary>
    public static class MurmurHash
    {
        private const ulong m = 0xc6a4a7935bd1e995ul;
        private const int r = 47;

        public static ulong ComputeHashToInt64(string data, ulong seed = 0xe17a1465ul)
        {
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data);
            ulong result = ComputeHashToInt64(bytes, seed);
            return result;
        }

        private static ulong ComputeHashToInt64(byte[] data, ulong seed = 0xe17a1465ul)
        {
            ulong len = (ulong)data.Length;
            ulong h = seed ^ (len * m);
            ulong nblocks = len / 8;
            int tail = (int)len & 7;

            for (ulong i = 0; i < nblocks; i++)
            {
                int indx = (int)i * 8;
                ulong k = System.BitConverter.ToUInt64(data, indx);

                k *= m;
                k ^= k >> r;
                k *= m;

                h ^= k;
                h *= m;
            }

            if (tail >= 7) h ^= (ulong)(data[nblocks * 8 + 6]) << 48;
            if (tail >= 6) h ^= (ulong)(data[nblocks * 8 + 5]) << 40;
            if (tail >= 5) h ^= (ulong)(data[nblocks * 8 + 4]) << 32;
            if (tail >= 4) h ^= (ulong)(data[nblocks * 8 + 3]) << 24;
            if (tail >= 3) h ^= (ulong)(data[nblocks * 8 + 2]) << 16;
            if (tail >= 2) h ^= (ulong)(data[nblocks * 8 + 1]) << 8;
            if (tail >= 1) h ^= (ulong)(data[nblocks * 8 + 0]);
            if (tail >= 1) h *= m;

            h ^= h >> r;
            h *= m;
            h ^= h >> r;

            return h;
        }

        public static string ComputeHashToString(string data)
        {
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data);
            byte[] buffer = ComputeHashToBytes(bytes);
            string result = BitConverter.ToString(buffer);
            return result;
        }

        public static string ComputeHashToString(byte[] data)
        {
            byte[] buffer = ComputeHashToBytes(data);
            string result = BitConverter.ToString(buffer);
            return result;
        }

        public static byte[] ComputeHashToBytes(string data)
        {
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data);
            byte[] result = ComputeHashToBytes(bytes);
            return result;
        }

        public static byte[] ComputeHashToBytes(byte[] data)
        {
            ulong uhash = ComputeHashToInt64(data);
            byte[] result = new byte[8];
            for (int i = 0; i < 8; i++)
            {
                result[i] = (byte)((uhash >> ((7 - i) * 8)) & 0xff);
            }
            return result;
        }
    }
}
